package chat.server;

import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ChatServer
{

    Map<String, SocketChannel> userVsChannelMap = new<String, SocketChannel> HashMap();

    Map<String, Integer> channelVsStatusMap = new<String, Integer> HashMap();

    Set<String> loggedInUsersList = new HashSet<String>();

    ServerSocketChannel serverSocketChannel;

    Selector selector;

    ExecutorService exeService;

    Properties props;

    public ChatServer()
    {
        props = new Properties();
        try
        {

            props.load(this.getClass().getResourceAsStream("/chat/server/Config.properties"));
            exeService = Executors.newFixedThreadPool(Integer.parseInt(props
                    .getProperty("max.handler.threads")));
        }
        catch (IOException e)
        {
            System.out.println("Failed to load config.properties");
            e.printStackTrace();
        }

    }

    public void startServer()
    {
        registerServerSocket();
        while (true)
        {
            try
            {
                System.out.println("Chat server is up and running");
                selector.select();
                handleSelectedKeys(selector.selectedKeys());
            }
            catch (IOException e)
            {
                e.printStackTrace();
                continue;
            }
            catch (Exception e)
            {
                e.printStackTrace();
                continue;
            }
        }

    }

    public void registerServerSocket()
    {
        try
        {
            serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.socket().bind(
                    new java.net.InetSocketAddress(props.getProperty("server.hostname"), Integer
                            .parseInt(props.getProperty("server.port"))));
            selector = Selector.open();
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
           // System.out.println("register ServerSocket done");
        }
        catch (IOException e)
        {
            e.printStackTrace();
             System.out.println("Fatal error in server startup. Not able to register server socket");
        }

    }

    public void handleSelectedKeys(Set selectedkeys) throws IOException
    {
        Iterator itr = selectedkeys.iterator();
        while (itr.hasNext())
        {
            SelectionKey key = (SelectionKey) itr.next();
            itr.remove();
            if (key.isAcceptable())
            {
               // System.out.println("got isAcceptable key");
                acceptAndRegister(key);
            }
            if (key.isReadable())
            {
               // System.out.println("got isReadable key");
                readMessage(key);
            }
        }
    }

    public void acceptAndRegister(SelectionKey key) throws IOException
    {
        SocketChannel client = serverSocketChannel.accept();
        client.configureBlocking(false);
        channelVsStatusMap.put(client.getRemoteAddress().toString(), 0);
        client.register(selector, SelectionKey.OP_READ);
    }

    public void readMessage(SelectionKey key) throws IOException
    {
        String remoteAddress = ((SocketChannel) key.channel()).getRemoteAddress().toString();
        if (channelVsStatusMap.get(remoteAddress) == 0)
        {
            //System.out.println("channel is free to read. no read operation in-progress");
            channelVsStatusMap.put(remoteAddress, 1);
            exeService.execute(new Handler(key, channelVsStatusMap, userVsChannelMap,
                    loggedInUsersList));
        }
    }

    public static void main(String[] args)
    {
        ChatServer server = new ChatServer();
        server.startServer();

    }

}
